package com.jacob.androidframe.database.dal;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.jacob.androidframe.database.DatabaseConfig;
import com.jacob.androidframe.database.SQLiteOpenAssistant;


import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;

/**
 * 本类封装和业务无关的数据库的DAL操作代码。 1个SQLiteDatabaseDAL子类只操作1个实体类。
 * 
 * 使用步骤
 * 1 新建一个类，继承于SQLiteDALBase<M>。注意将M声明为实体类名称。
 * 2 实现抽象方法。
 * 
 * @author 王超
 * 
 */
public abstract class SQLiteDALBase<M>
{
    // 实体类的Class对象
    private Class<M>            mModelClass;
    // 数据库开闭帮助类
    private SQLiteOpenAssistant mSQLiteOpenAssistant;
    // 数据库对象
    private SQLiteDatabase      mSQLiteDatabase;
    // 实体类对应的表名
    private String              mTableName;
    // 实体类对应的表中，所有的字段名
    private String[]            mAllColumnName;
    // 所有表字段和实体类成员变量的关联关系
    private Map<String, String> mFieldColumnMap;
    // 所有实体类成员变量和表字段的关联关系
    private Map<String, String> mColumnFieldMap;

    public SQLiteDALBase(Class<M> pModelClass)
    {
        mModelClass = pModelClass;
        mSQLiteOpenAssistant = SQLiteOpenAssistant.getInstance();
        mSQLiteDatabase = mSQLiteOpenAssistant.getReadableDatabase();

        mTableName = DatabaseConfig.getTableName(mModelClass);
        mAllColumnName = DatabaseConfig.getAllColumnName(mModelClass).toArray(new String[1]);
        mFieldColumnMap = DatabaseConfig.getFieldColumnMap(mModelClass);
        mColumnFieldMap = DatabaseConfig.getColumnFieldMap(mModelClass);
    }

    protected SQLiteOpenAssistant getSQLiteOpenAssistant()
    {
        return mSQLiteOpenAssistant;
    }

    /**
     * 返回数据库对象
     * 
     * @return 数据库对象
     */
    protected SQLiteDatabase getSqLiteDatabase()
    {
        if (mSQLiteDatabase == null) {
            mSQLiteDatabase = mSQLiteOpenAssistant.getReadableDatabase();
        }
        return mSQLiteDatabase;
    }

    /**
     * 得到实体类对应的表名
     * 
     * @return 实体类对应的表名
     */
    protected String getTableName()
    {
        return mTableName;
    }

    /**
     * 得到表中所有字段的名字
     * 
     * @return 包含所有字段名字的数组
     */
    protected String[] getAllColumnName()
    {
        return mAllColumnName;
    }

    /**
     * 返回表主键的名字。主键必须是表字段的第一个。
     * 
     * @return 表主键的名字
     */
    protected String getPrimaryColumnName()
    {
        return mAllColumnName[0];
    }

    /**
     * 返回表主键对应的实体类成员变量的值。
     * 
     * @return 主键对应的成员变量的值
     */
    protected long getPrimaryFieldValue(M pModelObject)
    {
        try {
            Field _PrimaryField = mModelClass.getDeclaredField(mColumnFieldMap.get(getPrimaryColumnName()));
            _PrimaryField.setAccessible(true);

            return _PrimaryField.getLong(pModelObject);

        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 设置表主键对应的实体类成员变量的值。
     * 
     */
    protected void setPrimaryFieldValue(M pModelObject, long pValue)
    {
        try {
            Field _PrimaryField = mModelClass.getDeclaredField(mColumnFieldMap.get(getPrimaryColumnName()));
            _PrimaryField.setAccessible(true);
            _PrimaryField.setLong(pModelObject, pValue);

        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 返回实体类成员变量和表字段的映射关系。
     * 
     * @return 返回Map对象，Key为实体类成员变量名，Value为字段名。
     */
    protected Map<String, String> getFieldColumnMap()
    {
        return mFieldColumnMap;
    }

    /**
     * 返回表字段和实体类成员变量的映射关系。
     * 
     * @return 返回Map对象，Key为字段名，Value为实体类成员变量名。
     */
    protected Map<String, String> getColumnFieldMap()
    {
        return mColumnFieldMap;
    }

    /**
     * 返回所有实体类对象，按照数据库默认排序。
     * 若需要其他排序，子类可以覆盖本方法。
     * 
     * @return 包含所有实体类对象的List
     */
    public List<M> select()
    {
        return select(null, null, null, null, null);
    }

    /**
     * 按照ID查找实体类对象。
     * 
     * @param pID
     *            ID主键
     * @return 实体类对象，若未找到返回null。
     */
    public M select(long pID)
    {
        String _PrimaryKeyName = getPrimaryColumnName();
        List<M> _ModelList = select(_PrimaryKeyName + "=" + pID, null, null, null, null);

        if (_ModelList.size() <= 0) {
            return null;
        } else {
            return _ModelList.get(0);
        }
    }

    /**
	 * 将实体类对象插入表中。
	 * 若插入成功，会将Rowid设置到ModelObject的ID中。
	 * 
	 * @param pModelObject
	 *            实体类对象
	 * @return 插入成功返回true，插入失败返回false。
	 */
	public boolean insert(M pModelObject)
	{
	    long _RowID = mSQLiteDatabase.insert(mTableName, null, toContentValues(pModelObject));
	
	    if (_RowID != -1) {
	        setPrimaryFieldValue(pModelObject, _RowID);
	        return true;
	    } else {
	        return false;
	    }
	
	}

	/**
	 * 将多个实体类对象插入表中。
	 * 
	 * @param pModelObjectList
	 *            包含实体类对象的List
	 * @return List，其中包含插入结果。
	 */
	public List<Boolean> insert(List<M> pModelObjectList)
	{
	    List<Boolean> _ResultList = new ArrayList<Boolean>();
	
	    for (M _ModelObject : pModelObjectList) {
	    	_ResultList.add(insert(_ModelObject));
	    }
	
	    return _ResultList;
	}

	/**
     * 更新数据库中实体类对象对应的记录
     * 
     * @param pModelObject
     *            实体类对象
     * @return 更新成功返回true，更新失败返回false。
     */
    public boolean update(M pModelObject)
    {
        String _PrimaryKeyName = getPrimaryColumnName();
        long _PrimaryKeyValue = getPrimaryFieldValue(pModelObject);

        boolean _Result = false;
        
        beginTransaction();
        try {
            int _UpdateRowNumbers = update(pModelObject, _PrimaryKeyName + "=" + _PrimaryKeyValue, null);
            if (_UpdateRowNumbers == 1) {
                setTransactionSuccessful();
                _Result = true;
            }
        } finally {
            endTransaction();
        }
        
        return _Result;
    }

    /**
     * 插入或更新实体类对象。
     * 若实体类对象的主键ID<=0，则插入；若>0，则更新。
     * 
     * @param pModelObject
     *            实体类对象
     * @return 插入/更新成功返回true，插入/更新失败返回false。
     */
    public boolean insertOrUpdateModel(M pModelObject)
    {
        if (getPrimaryFieldValue(pModelObject) <= 0) {
            return insert(pModelObject);
        } else {
            return update(pModelObject);
        }
    }

    /**
     * 从数据库中删除实体类对象
     * 
     * @param pID
     *            实体类对象的主键ID
     * @return 删除成功返回true，删除失败返回false。
     */
    public boolean deleteModel(long pID)
    {
        String _PrimaryKeyName = getPrimaryColumnName();
        String _Selection = _PrimaryKeyName + "=" + pID;

        boolean _Result = false;
        
        beginTransaction();
        try {
            int _DeleteRowNumbers = delete(_Selection, null);
            if (_DeleteRowNumbers == 1) {
                setTransactionSuccessful();
                _Result = true;
            }
        } finally {
            endTransaction();
        }
        
        return _Result;
    }

    /**
     * 从数据库中删除实体类对象，若删除成功，则会将实体类对象的ID设置为-1。
     * 
     * @param pModelObject
     *            实体类对象
     * @return 删除成功返回true，删除失败返回false。
     */
    public boolean deleteModel(M pModelObject)
    {
    	boolean _Reslut = deleteModel(getPrimaryFieldValue(pModelObject));
    	
    	if (_Reslut) {
    		setPrimaryFieldValue(pModelObject, -1);
		}
    	
        return _Reslut;
    }
    
    /**
     * 清空表中所有记录。
     */
    public void clearTable()
    {
    	mSQLiteDatabase.delete(mTableName, null, null);
    }
    
    /**
	 * 查找实体类对象，支持排序。默认查询所有字段。
	 * 
	 * @param pSelection
	 *            WHERE条件（不包括WHERE关键字），传入null返回所有行。
	 * @param pSelectionArgs
	 *            一个字符串数组，系统会用数组元素依次替换参数3中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
	 * @param pGroupBy
	 * @param pHaving
	 * @param pOrderBy
	 * @return 包含所有实体类对象的List。
	 */
	protected List<M> select(String pSelection, String[] pSelectionArgs, String pGroupBy, String pHaving, String pOrderBy)
	{
	    List<M> _ModelObjectList = new ArrayList<M>();
	    Cursor _Cursor = mSQLiteDatabase.query(mTableName, mAllColumnName, pSelection, pSelectionArgs, pGroupBy, pHaving, pOrderBy);
	    
	    for (; _Cursor.moveToNext();) {
	        _ModelObjectList.add(toModel(_Cursor));
	    }
	
	    _Cursor.close();
	
	    return _ModelObjectList;
	}

	/**
	 * 统计表中符合条件的记录个数。
	 * 默认查询所有字段，效率较低，子类可以覆盖。
	 * 
	 * @param pSelection
	 *            WHERE条件（不包括WHERE关键字），传入null返回所有行。
	 * @param pSelectionArgs
	 *            一个字符串数组，系统会用数组元素依次替换pSelection中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
	 * @return 目标表中，符合条件的记录个数。
	 */
	protected int getCount(String pSelection, String[] pSelectionArgs)
	{
	    Cursor _Cursor = this.getSqLiteDatabase().query(mTableName, mAllColumnName, pSelection, pSelectionArgs, null, null, null);
	    int _Count = _Cursor.getCount();
	    _Cursor.close();
	    return _Count;
	}

	/**
	 * 修改实体类对象对应的记录
	 * 
	 * @param pSelection
	 *            WHERE条件（不包括WHERE关键字），传入null更新所有行。
	 * @param pSelectionArgs
	 *            一个字符串数组，系统会用数组元素依次替换参数3中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
	 * @return 更新记录的条数。
	 */
	protected int update(M pModelObject, String pSelection, String[] pSelectionArgs)
	{
	    return mSQLiteDatabase.update(mTableName, toContentValues(pModelObject), pSelection, pSelectionArgs);
	}

	/**
     * 删除表中符合条件的记录
     * 
     * @param pSelection
     *            WHERE条件（不包括WHERE关键字），传入null删除所有行。
     * @param pSelectionArgs
     *            一个字符串数组，系统会用数组元素依次替换pSelection中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
     * @return
     *         若传入pSelection参数，则返回删除的记录条数；若pSelection传入null，返回0。若要删除所有行，并返回删除的记录条数，pSelection应传入1。
     */
    protected int delete(String pSelection, String[] pSelectionArgs)
    {
        return this.getSqLiteDatabase().delete(mTableName, pSelection, pSelectionArgs);
    }

    /**
     * 执行一句非查詢、没有返回值的SQL语句。例如：建表、插入、刪除等。
     * 
     * @param pSQL
     *            SQL语句
     * @return 执行成功返回true，执行失败输出错误，并返回false。
     */
    protected boolean execSQL(String pSQL)
    {
        boolean _Result = false;

        try {
            mSQLiteDatabase.execSQL(pSQL);
            _Result = true;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return _Result;
    }

    /**
     * 开始数据库事务。
     * Transactions can be nested. When the outer transaction is ended all of the work done in that transaction and all of the nested transactions will be committed or
     * rolled back. The changes will be rolled back if any transaction is ended without being marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
     * 
     * <p>
     * Here is the standard idiom for transactions:
     * 
     * <pre>
     *   DAL.beginTransaction();
     *   try {
     *     ...
     *     DAL.setTransactionSuccessful();
     *   } finally {
     *     DAL.endTransaction();
     *   }
     * </pre>
     */
    protected void beginTransaction()
    {
        mSQLiteDatabase.beginTransaction();
    }

    /**
     * 设置数据库事务执行成功
     */
    protected void setTransactionSuccessful()
    {
        mSQLiteDatabase.setTransactionSuccessful();
    }

    /**
     * 结束数据库事务
     */
    protected void endTransaction()
    {
        mSQLiteDatabase.endTransaction();
    }

    /**
     * 将实体类对象转换成ContentValues对象，便于insert和update。
     * 转换的时候不要包含主键。
     * 
     * @return ContentValues对象
     */
    protected abstract ContentValues toContentValues(M pModelObject);

    /**
     * 将当前查询结果，转换成实体类对象。
     * 子类实现时，注意不要改变游标的位置，也不要关闭游标。
     * 
     * @param pCursor
     *            游标
     * @return 实体类对象
     */
    protected abstract M toModel(Cursor pCursor);

}
